package com.ht.db.router.aop;

import com.ht.db.router.annotation.DBRouter;
import com.ht.db.router.config.DBRouterProperties;
import com.ht.db.router.core.DBRouterStrategyHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * aop切面拦截，用作数据库表的切换
 */
@Component
@Aspect
@Slf4j
public class DBRouterAspect {

    private final DBRouterProperties dbRouterProperties;

    private final DBRouterStrategyHandler dbRouterStrategyHandler;

    public DBRouterAspect(DBRouterProperties dbRouterProperties, DBRouterStrategyHandler dbRouterStrategyHandler) {
        this.dbRouterProperties = dbRouterProperties;
        this.dbRouterStrategyHandler = dbRouterStrategyHandler;
    }

    @Around("@annotation(dbRouter)")
    public Object dbRouterAround(ProceedingJoinPoint joinPoint, DBRouter dbRouter) throws Throwable {
        String dbKey = StringUtils.isBlank(dbRouter.key()) ? dbRouterProperties.getRouterKey() : dbRouter.key();
        if (StringUtils.isBlank(dbKey)) {
            throw new RuntimeException("dbRouter dbKey is necessary!");
        }
        //1. 查询dbKey对应的值
        String attrValue = getAttrValue(dbKey, joinPoint.getArgs());
        //2. 设置分库router
        dbRouterStrategyHandler.doRouter(attrValue);
        try {
            return joinPoint.proceed();
        } finally {
            dbRouterStrategyHandler.clear();
        }
    }

    /**
     * 从方法的入参里面查找对应属性的值返回
     */
    public String getAttrValue(String attrName, Object[] args) {
        //一个参数就把这个参数作为分库的字段
        if (1 == args.length) {
            Object arg = args[0];
            if (arg instanceof String) {
                return arg.toString();
            }
        }
        //循环每个参数查找对应
        for (Object arg : args) {
            Object fieldValue = getFieldValue(arg, attrName);
            if (fieldValue != null) {
                return fieldValue.toString();
            }
        }
        return null;
    }

    /**
     * 根据字段获取对象里面的值
     */
    public Object getFieldValue(Object obj, String attrName) {
        Field field;
        try {
            try {
                field = obj.getClass().getDeclaredField(attrName);
            } catch (NoSuchFieldException e) {
                field = obj.getClass().getSuperclass().getDeclaredField(attrName);
            }
        } catch (NoSuchFieldException e) {
            log.error("dbRouter can't find field from arg object,obj{},attrName:{}", obj, attrName);
            return null;
        }
        field.setAccessible(true);
        try {
            return field.get(obj);
        } catch (IllegalAccessException e) {
            log.error("can't find attrName-:{}'s value in obj:{}", attrName, obj);
            return null;
        } finally {
            field.setAccessible(false);
        }
    }

}
